Kullanıcıdan Token Uzantılarını Kullanma
Özet
Token Uzantıları Programı
,Token Programı
ile aynı işlevlerin yanı sırauzantılar
ekler.- Bu iki token programı:
Token Programı
veToken Uzantıları Programı
, ayrı adresler kullanır ve doğrudan uyumlu değildir. - Her ikisini desteklemek, müşteri tarafındaki işlevlerde doğru program kimliğini belirtmeyi gerektirir.
- Varsayılan olarak, SPL program kütüphanesi, başka bir program belirtilmedikçe orijinal
Token Programı
kullanır. Token Uzantıları Programı
, ayrıca teknik özel adı olanToken22
olarak da anılabilir.
Genel Bakış
Token Uzantıları Programı
, orijinal Token Programı
nı, uzantılar olarak bilinen ek özellikler ile geliştirmektedir. Bu uzantılar, önceki durumları ele almak için tasarlanmıştır ve geliştiricilerin Solana Program Kütüphanesini çatal yapıp değiştirmesiyle sonuçlanan ayrık ekosistemler ve benimseme zorlukları yaratmıştır. Token Uzantıları Programının tanıtımı, bu durumların etkili bir şekilde ele alınmasını sağlar.
Not:
Token Programı
veToken Uzantıları Programı
farklı on-chain programları oldukları için uyumlu değildir. Örneğin,Token Uzantıları Programı
ile çıkarılan bir token,Token Programı
ile transfer edilemeyebilir. Sonuç olarak, tüm SPL token'larını görüntülemek veya desteklemek için gerekli olan herhangi bir müşteri tarafı uygulamasında her iki programı da desteklememiz gerekecek. Bu, orijinal Token Programı'ndan (adres:TOKEN_PROGRAM_ID
) ve Uzantı Programı'ndan (adres:TOKEN_2022_PROGRAM_ID
) mintleri açıkça işleyeceğimiz anlamına gelir.
Neyse ki, iki programın arayüzleri tutarlıdır ve bu, yalnızca program kimliğini değiştirerek spl-token
yardımcı işlevlerinin her iki programda da kullanılabilmesini sağlar (fonksiyon, herhangi bir program kimliği sağlanmadığı takdirde varsayılan olarak orijinal Token Programını kullanır). Çoğu zaman, son kullanıcılar belirli token programının kullanılmasından endişe duymazlar. Bu nedenle, her iki token çeşidinden gelen detayları takip etmek, derlemek ve birleştirmek için ek bir mantık uygulamak, akıcı bir kullanıcı deneyimi sağlamak açısından önemlidir.
Son olarak, "Token 22" genellikle teknik ad olarak kullanılmaktadır. Birinin Token 22 Programı'na atıfta bulunduğunu gördüğünüzde, Token Uzantıları Programı'na atıfta bulunduğunu bilmelisiniz.
Token Programı Token'ları ile Token Uzantıları Token'ları Arasındaki Farklar
Uyarı: Mintler ve token'larla etkileşimde bulunurken, doğru Token Programını kullandığımızdan emin olmamız gerekir. Bir
Token Programı
minti oluşturmak içinToken Programı
kullanın; uzantılar ile bir mint oluşturmak içinToken Uzantıları Programı
nı kullanın.
Neyse ki, spl-token
paketi bunu basit hale getiriyor. Hem TOKEN_PROGRAM_ID
hem de TOKEN_2022_PROGRAM_ID
sabitlerini sağlar ve program kimliği girdi olarak alan tüm token oluşturma ve mintleme yardımcı işlevlerine sahiptir.
Not
spl-token
, aksi belirtilmediği sürece TOKEN_PROGRAM_ID
kullanmayı varsayılan olarak ayarlar. Token Uzantıları Programı ile ilgili tüm fonksiyon çağrıları için TOKEN_2022_PROGRAM_ID
'yi açıkça geçtiğinizden emin olun. Aksi takdirde, şu hatayı alırsınız: TokenInvalidAccountOwnerError
.
Hem Token hem de Uzantı Token'ları ile Çalışırken Dikkat Edilmesi Gerekenler
Her ne kadar bu iki programın arayüzleri tutarlı kalsa da, iki farklı programdır. Bu programların program kimlikleri benzersiz ve değiştirilemezdir; bu da kullanıldıklarında farklı adreslerle sonuçlanır. Hem Token Programı
token'larını hem de Token Uzantıları Programı
token'larını desteklemek istiyorsanız, müşteri tarafında ek bir mantık eklemeniz gerekir.
İlişkili Token Hesapları (ATA)
Bir İlişkili Token Hesabı (ATA), adresi, cüzdanın ortak anahtarına, token'ın mintine ve token programına dayanarak türetilen bir Token Hesabıdır. Bu mekanizma, her bir mint için kullanıcı başına belirleyici bir Token Hesabı adresi sağlar. ATA hesabı genellikle çoğu sahip için varsayılan hesaptır. Neyse ki, ATAlar her iki token programı ile de aynı şekilde işlenmektedir.
Her token programı için ATA yardımcı işlevlerini sağlamak için istediğimiz program kimliğini verebiliriz.
getOrCreateAssociatedTokenAccount
çağrısında Token Uzantıları Programını kullanmak istiyorsak, tokenProgramId
parametresi için TOKEN_2022_PROGRAM_ID
'yi geçirebiliriz:
const tokenProgramId = TOKEN_2022_PROGRAM_ID;
const tokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mintAddress,
payer.publicKey,
true,
"finalized",
{ commitment: "finalized" },
tokenProgramId, // Token Program token'ları için TOKEN_PROGRAM_ID ve Token Uzantıları Programı token'ları için TOKEN_2022_PROGRAM_ID
);
ATA'nın adresini baştan oluşturmak için doğru tohumları sağlayarak findProgramAddressSync
fonksiyonunu kullanabiliriz:
function findAssociatedTokenAddress(
walletAddress: PublicKey,
tokenMintAddress: PublicKey,
): PublicKey {
return PublicKey.findProgramAddressSync(
[
walletAddress.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(), // TOKEN_2022_PROGRAM_ID ile Token22 token'ları için TOKEN_PROGRAM_ID'yi değiştirin
tokenMintAddress.toBuffer(),
],
ASSOCIATED_TOKEN_PROGRAM_ID,
)[0];
}
Token'ları Fetch Etme
Token Programı veya Token Uzantıları Programı ile yapılmış token'lar arasında herhangi bir fark yoktur. Bu, token'ları fetch etme konusunda Token Programı veya Token Uzantıları Programı token'ları arasında bir fark olmadığı anlamına gelir. Tek yapmamız gereken doğru token programını sağlamak:
const tokenAccounts = await connection.getTokenAccountsByOwner(
walletPublicKey,
{ programId: TOKEN_PROGRAM_ID }, // veya TOKEN_2022_PROGRAM_ID
);
Belirli bir sahibin tüm token’larını fetch etmek istiyorsak, getTokenAccountsByOwner
gibi bir fonksiyonu kullanabiliriz ve bunu iki kez çağırabiliriz; bir kez TOKEN_PROGRAM_ID
ile ve bir kez TOKEN_2022_PROGRAM_ID
ile.
const allOwnedTokens = [];
const tokenAccounts = await connection.getTokenAccountsByOwner(
wallet.publicKey,
{ programId: TOKEN_PROGRAM_ID },
);
const tokenExtensionAccounts = await connection.getTokenAccountsByOwner(
wallet.publicKey,
{ programId: TOKEN_2022_PROGRAM_ID },
);
allOwnedTokens.push(...tokenAccounts, ...tokenExtensionAccounts);
Not Token'ı fetch ettikten sonra, token programını saklamak ve token ile ilişkilendirmek isteyebilirsiniz.
Sahiplik Programını Kontrol Etme
Belirli bir hesap için token programını bilmediğiniz bir senaryo ile karşılaşabilirsiniz. Neyse ki, getParsedAccountInfo
bize sahiplik programını belirlememize izin verecektir:
const accountInfo = await connection.getParsedAccountInfo(mintAddress);
if (accountInfo.value === null) {
throw new Error("Hesap bulunamadı");
}
const programId = accountInfo.value.owner; // Token Programı mint adresi için TOKEN_PROGRAM_ID ve Token Uzantıları Programı mint adresi için TOKEN_2022_PROGRAM_ID döndürecektir
// Şimdi programId'yi kullanarak token'ları fetch ediyoruz
const tokenAccounts = await connection.getTokenAccountsByOwner(
wallet.publicKey,
{ programId },
);
Not Sahiplik programını fetch ettikten sonra, o sahipliği saklamak ve mintler/token'larla ilişkilendirmek iyi bir fikir olabilir.
Laboratuvar - Bir Script'e Uzantı Token Desteği Ekleme
Mevcut bir script'e Token Uzantıları desteği eklediğimiz bütünsel bir örnek üzerinden çalışalım. Bu laboratuvar, orijinal Token Programı ve uzantı karşıtı için yetenekleri ve ince ayrıntıları benimsemek için gerekli ayarlara ve genişletmelere rehberlik edecektir.
Bu laboratuvarın sonunda, bu iki ayrı ama ilişkili token sistemini desteklemenin karmaşıklıklarına yol almış olacağız ve scriptimizin her iki sistemle de sorunsuz bir şekilde etkileşimde bulunmasını sağlayacağız.
1. Başlangıç kodunu klonlayın
Başlamak için, bu laboratuvarın deposunu klonlayın ve starter
dalına geçin. Bu dal, sizi başlatmak için bazı yardımcı dosyalar ve biraz şablon kod içerir.
git clone https://github.com/Unboxed-Software/solana-lab-token22-in-the-client.git
cd solana-lab-token22-in-the-client
git checkout starter
Bağımlılıkları kurmak için npm install
komutunu çalıştırın.
2. Başlangıç koduyla tanışın
Başlangıç kodu aşağıdaki dosyalarla birlikte gelir:
print-helpers.ts
index.ts
print-helpers.ts
dosyası, verileri yapılandırılmış bir formatta konsola çıktı vermek üzere tasarlanmış printTableData
adında bir fonksiyon içerir. Bu fonksiyon, herhangi bir nesneyi argüman olarak kabul edebilir ve verileri okunabilir bir tablo formatında görüntülemek için NodeJS tarafından sağlanan console.table
yöntemini kullanır.
Son olarak, index.ts
ana scriptimizi içerir. Şu anda yalnızca bir bağlantı oluşturur ve payer
için anahtar çiftini oluşturmak üzere initializeKeypair
fonksiyonunu çağırır.
3. Doğrulayıcı düğümünü çalıştırın (İsteğe bağlı)
İsteğe bağlı olarak, devnet yerine kendi yerel doğrulayıcınızı çalıştırmak isteyebilirsiniz. Bu, havale ile ilgili herhangi bir sorunu aşmanın iyi bir yoludur.
Ayrı bir terminalde, aşağıdaki komutu çalıştırın: solana-test-validator
. Bu, düğümü çalıştırır ve bazı anahtarlar ve değerler de kaydeder. Bağlantımızda kullanmamız gereken değer, bu durumda http://127.0.0.1:8899
olan JSON RPC URL'sidir. Bunu bağlantıda yerel RPC URL'sini kullanmak için kullanıyoruz.
const connection = new Connection("http://127.0.0.1:8899", "confirmed");
Devnet'i kullanmak ve kendi devnet cüzdanınızı sağlamak istiyorsanız hala yapabilirsiniz - sadece Connection
ve initializeKeypair
'ye anahtar çiftini sağlamaya yeniden yapılandırın.
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
Şu ana kadar her şeyin düzgün çalışıp çalışmadığını test edelim ve npm run start
komutunu çalıştıralım. Terminalinizde payer
kamu anahtarının kaydedildiğini görmelisiniz.
4. Token Programı ve Token Uzantıları Programı mintlerini oluşturun
Token Programı ve Token Uzantıları Programı kullanarak yeni token mintleri oluşturmaya başlayalım.
Yeni bir dosya oluşturun ve adını create-and-mint-token.ts
koyun. Bu dosyada createAndMintToken
adında bir fonksiyon oluşturacağız. İsimden de anlaşılacağı gibi, bir mint oluşturacak, token hesabı (ATA) oluşturacak ve ardından bu hesaba bir miktar token mintleyecek.
Bu createAndMintToken
fonksiyonu içinde createMint
, getOrCreateAssociatedTokenAccount
ve mintTo
çağrıları yapılacaktır. Bu fonksiyon, kullanılan belirli token programından bağımsız olacak şekilde tasarlanmıştır; bu, tokenların yalnızca Token Programı
veya Token Uzantıları Programı
ile oluşturulmasını sağlar. Bu yetenek, sağlanan program kimliğine dayalı olarak davranışını uyarlamak için bir program kimliğini parametre olarak kabul ederek elde edilir.
Bu fonksiyona geçeceğimiz argümanlar şunlardır:
connection
- Kullanılacak bağlantı nesnesitokenProgramId
- İşaret edilecek token programıpayer
- İşlemi ödeyen anahtar çiftidecimals
- Mint için dahil edilecek ondalık sayısımintAmount
- Ödeyiciye mint edilecek token miktarı
Ve bu, fonksiyonun yapacağı işlemlerdir:
createMint
kullanarak yeni bir mint oluşturun.getMint
ile mint bilgisini alın.printTableData
ile mint bilgilerini kaydedin.getOrCreateAssociatedTokenAccount
ile ilişkili bir token hesabı oluşturun.- İlişkili token hesabının adresini kaydedin.
mintTo
ile ilişkili token hesabına token mintleyin.
Bunların hepsi, son createAndMintToken
fonksiyonunun görünümüdür:
import {
createMint,
getMint,
getOrCreateAssociatedTokenAccount,
mintTo,
} from "@solana/spl-token";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import printTableData from "./print-helpers";
export async function createAndMintToken(
connection: Connection,
tokenProgramId: PublicKey,
payer: Keypair,
decimals: number,
mintAmount: number,
): Promise<PublicKey> {
console.log("\\nYeni bir mint oluşturuluyor...");
const mint = await createMint(
connection,
payer,
payer.publicKey,
payer.publicKey,
decimals,
undefined,
{
commitment: "finalized", // confirmOptions argümanı
},
tokenProgramId,
);
console.log("\\nMint bilgisi alınıyor...");
const mintInfo = await getMint(connection, mint, "finalized", tokenProgramId);
printTableData(mintInfo);
console.log("\\nİlişkili token hesabı oluşturuluyor...");
const tokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mint,
payer.publicKey,
true,
"finalized",
{ commitment: "finalized" },
tokenProgramId,
);
console.log(`İlişkili token hesabı: ${tokenAccount.address.toBase58()}`);
console.log("\\nİlişkili token hesabına mintleme yapılıyor...");
await mintTo(
connection,
payer,
mint,
tokenAccount.address,
payer,
mintAmount,
[payer],
{ commitment: "finalized" },
tokenProgramId,
);
return mint;
}
export default createAndMintToken;
5. Token oluşturma ve mintleme
Şimdi yeni fonksiyonumuzu index.ts
ana scriptimizde iki kez çağırarak kullanacağız. Hem bir Token Programı
hem de Token Uzantıları Programı
token'ını test etmek isteyeceğiz. Yani, iki farklı program kimliğimizi kullanacağız:
import { initializeKeypair } from "@solana-developers/helpers";
import { Cluster, Connection } from "@solana/web3.js";
import createAndMintToken from "./create-and-mint-token";
import printTableData from "./print-helpers";
import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import dotenv from "dotenv";
dotenv.config();
const connection = new Connection("http://127.0.0.1:8899", "confirmed");
const payer = await initializeKeypair(connection);
console.log(`Ödeyen: ${payer.publicKey.toBase58()}`);
const tokenProgramMint = await createAndMintToken(
connection,
TOKEN_PROGRAM_ID,
payer,
0,
1000,
);
const tokenExtensionProgramMint = await createAndMintToken(
connection,
TOKEN_2022_PROGRAM_ID,
payer,
0,
1000,
);
Bu noktada npm run start
komutunu çalıştırarak her iki mintin de oluşturulduğunu ve bilgilerin konsola kaydedildiğini görebilirsiniz.
6. Token Programı ve Token Uzantıları Programı token'larını fetch etme
Artık token'ları cüzdanın ortak anahtarını ve program kimliğini kullanarak fetch edebiliriz.
Yeni bir dosya oluşturun, adını fetch-token-info.ts
koyun.
Bu yeni dosya içerisinde, fetchTokenInfo
fonksiyonunu oluşturun. Bu fonksiyon, sağlanan token hesabını fetch edecek ve yeni bir arayüz oluşturacak TokenInfoForDisplay
döndürecektir. Bu, dönen verileri konsolda güzel bir şekilde formatlamak için kullanılacaktır. Yine, bu fonksiyon, hesabın hangi token programına ait olduğuna dair kayıtsız olacaktır.
import { AccountLayout, getMint } from "@solana/spl-token";
import { Connection, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
export type TokenTypeForDisplay = "Token Program" | "Token Uzantıları Programı";
export interface TokenInfoForDisplay {
mint: PublicKey;
amount: number;
decimals: number;
displayAmount: number;
type: TokenTypeForDisplay;
}
Tüm bu bilgileri fetch etmek için getTokenAccountsByOwner
çağrısı yapacak ve sonuçları yeni TokenInfoForDisplay
arayüzüne dönüştüreceğiz.
fetchTokenInfo
fonksiyonu için gereken parametreler şunlardır:
connection
- Kullanılacak bağlantı nesnesiowner
- İlişkili token hesaplarının sahibi olan cüzdanprogramId
- Belirtilmesi gereken token programıtype
- YaToken Programı
ya daToken Uzantıları Programı
; konsolda kaydetme amacıyla kullanılır.
export type TokenTypeForDisplay = "Token Program" | "Token Uzantıları Programı";
export interface TokenInfoForDisplay {
mint: PublicKey;
amount: number;
decimals: number;
displayAmount: number;
type: TokenTypeForDisplay;
}
export async function fetchTokenInfo(
connection: Connection,
owner: PublicKey,
programId: PublicKey,
type: TokenTypeForDisplay,
): Promise<TokenInfoForDisplay[]> {
const tokenAccounts = await connection.getTokenAccountsByOwner(owner, {
programId,
});
const ownedTokens: TokenInfoForDisplay[] = [];
for (const tokenAccount of tokenAccounts.value) {
const accountData = AccountLayout.decode(tokenAccount.account.data);
const mintInfo = await getMint(
connection,
accountData.mint,
"finalized",
programId,
);
ownedTokens.push({
mint: accountData.mint,
amount: Number(accountData.amount),
decimals: mintInfo.decimals,
displayAmount: Number(accountData.amount) / 10 ** mintInfo.decimals,
type,
});
}
return ownedTokens;
}
Bu fonksiyonu uygulamak için, önceki index.ts
'ye iki ayrı çağrı ekleyelim:
// önceki importlar
import { TokenInfoForDisplay, fetchTokenInfo } from "./fetch-token-info";
// önceki kod
const myTokens: TokenInfoForDisplay[] = [];
myTokens.push(
...(await fetchTokenInfo(
connection,
payer.publicKey,
TOKEN_PROGRAM_ID,
"Token Programı",
)),
...(await fetchTokenInfo(
connection,
payer.publicKey,
TOKEN_2022_PROGRAM_ID,
"Token Uzantıları Programı",
)),
);
printTableData(myTokens);
npm run start
komutunu çalıştırdığınızda, artık ödeyici cüzdanının sahip olduğu tüm token’ların listesi gösterilecektir.
7. Token Programı ve Token Uzantıları Programı token'larını program kimliği olmadan fetch etme
Artık belirli bir mint hesabından sahiplik programını nasıl alacağımıza bir göz atalım.
Bunu yapmak için, fetch-token-info.ts
dosyasına yeni bir fetchTokenProgramFromAccount
fonksiyonu oluşturacağız. Bu fonksiyon, bize verilen mint'in programId
'sini basitçe döndürecektir.
Bunu gerçekleştirmek için getParsedAccountInfo
fonksiyonunu çağıracak ve .value.owner
dan sahiplik programını döndüreceğiz.
fetchTokenProgramFromAccount
fonksiyonu için gereken parametreler şunlardır:
connection
- Kullanılacak bağlantı nesnesimint
- Mint hesabının ortak anahtarı
Son fonksiyon görünümü şu şekilde olacaktır:
// önceki importlar ve kod
export async function fetchTokenProgramFromAccount(
connection: Connection,
mint: PublicKey,
) {
// Mintten program ID'sini bularak döndür
const accountInfo = await connection.getParsedAccountInfo(mint);
if (accountInfo.value === null) {
throw new Error("Hesap bulunamadı");
}
const programId = accountInfo.value.owner;
return programId;
}
Son olarak, index.ts
dosyamıza buna uygun bir örnek ekleyelim:
// önceki importlar
import {
TokenInfoForDisplay,
fetchTokenInfo,
fetchTokenProgramFromAccount,
} from "./fetch-token-info";
// önceki kod
const tokenProgramTokenProgram = await fetchTokenProgramFromAccount(
connection,
tokenProgramMint,
);
const tokenExtensionProgramTokenProgram = await fetchTokenProgramFromAccount(
connection,
tokenExtensionProgramMint,
);
if (!tokenProgramTokenProgram.equals(TOKEN_PROGRAM_ID))
throw new Error("Token Program mint token programı doğru değil");
if (!tokenExtensionProgramTokenProgram.equals(TOKEN_2022_PROGRAM_ID))
throw new Error("Token Uzantıları Programı mint token programı doğru değil");
Son kez, npm run start
komutunu yeniden çalıştırdığınızda aynı çıktıyı göreceksiniz - bu, beklenen token programlarının doğru olduğunu gösterir.
Hepsi bu kadar! Eğer herhangi bir adımda takılırsanız, bu laboratuvarın deposundaki solution
dalında tamamlanmış kodu bulabilirsiniz.
Mücadele
Mücadele olarak, Token Programı token'ları ve Token Uzantıları token'ları için token yakma işlevini uygulamaya çalışın.